package device

import (
	"context"
	"encoding/base64"
	"strings"

	"device-admin/internal/app/grpc/protoc/devicec"
	"device-admin/internal/app/grpc/service/pubsub"
	"device-admin/internal/dal"
	"device-admin/internal/dao"
	"device-admin/internal/models"
	"device-admin/internal/pkg/convert"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"github.com/samber/lo"
	"google.golang.org/protobuf/proto"
)

var ValidServerUrl = []string{
	"v3.dev.iybot.com",  // 开发环境-旧
	"v3.test.iybot.com", // 测试环境-旧
	"dev.iybot.com",     // 开发地址
	"test.iybot.com",    // 测试地址
	"www.iybot.com",     // 生产环境
	"sg.iybot.com",      // 新加坡站点
}

type TerminalConfig struct{}

// CreateConfigs97 保存97配置
func (TerminalConfig) CreateConfigs97(ctx context.Context, robotID string, config *devicec.Config97) error {
	// 全部待处理的配置
	records := []*models.TerminalConfig{}
	records = append(records,
		[]*models.TerminalConfig{
			{RobotID: robotID, TerminalType: "97", Key: "server_url", Value: config.GetServerUrl()},
			{RobotID: robotID, TerminalType: "97", Key: "max_purchase_num", Value: convert.ConvertToString(config.GetMaxPurchaseNum())},
			{RobotID: robotID, TerminalType: "97", Key: "pickup_timeout", Value: convert.ConvertToString(config.GetPickupTimeout())},
			{RobotID: robotID, TerminalType: "97", Key: "enable_upload_faceshot", Value: convert.ConvertToString(config.GetEnableUploadFaceshot())},
			{RobotID: robotID, TerminalType: "97", Key: "enable_ignore_overload", Value: convert.ConvertToString(config.GetEnableIgnoreOverload())},
			{RobotID: robotID, TerminalType: "97", Key: "machine_version", Value: convert.ConvertToString(config.GetMachineVersion())},
			{RobotID: robotID, TerminalType: "97", Key: "enable_standby_page", Value: convert.ConvertToString(config.GetEnableStandbyPage())},
			{RobotID: robotID, TerminalType: "97", Key: "enable_side_camera", Value: convert.ConvertToString(config.GetEnableSideCamera())},
			{RobotID: robotID, TerminalType: "97", Key: "enable_nav", Value: convert.ConvertToString(config.GetEnableNav())},
			{RobotID: robotID, TerminalType: "97", Key: "disable_unempty_deliver", Value: convert.ConvertToString(config.GetDisableUnemptyDeliver())},
			{RobotID: robotID, TerminalType: "97", Key: "enable_voice_interaction", Value: convert.ConvertToString(config.GetEnableVoiceInteraction())},
			{RobotID: robotID, TerminalType: "97", Key: "enable_log_print", Value: convert.ConvertToString(config.GetEnableLogPrint())},
			{RobotID: robotID, TerminalType: "97", Key: "enable_important_statement", Value: convert.ConvertToString(config.GetEnableImportantStatement())},
			{RobotID: robotID, TerminalType: "97", Key: "goods_refund_remark", Value: config.GetGoodsRefundRemind()},
			{RobotID: robotID, TerminalType: "97", Key: "reboot_hour", Value: convert.ConvertToString(config.GetRebootHour())},
			{RobotID: robotID, TerminalType: "97", Key: "reboot_min", Value: convert.ConvertToString(config.GetRebootMin())},
			{RobotID: robotID, TerminalType: "97", Key: "deliver_identify_method", Value: convert.ConvertToString(config.GetDeliverIdentifyMethod())},
			{RobotID: robotID, TerminalType: "97", Key: "enable_game", Value: convert.ConvertToString(config.GetEnableGame())},
			{RobotID: robotID, TerminalType: "97", Key: "game_win_odds", Value: convert.ConvertToString(config.GetGameWinOdds())},
			{RobotID: robotID, TerminalType: "97", Key: "game_lose_odds", Value: convert.ConvertToString(config.GetGameLoseOdds())},
			{RobotID: robotID, TerminalType: "97", Key: "game_draw_odds", Value: convert.ConvertToString(config.GetGameDrawOdds())},
			{RobotID: robotID, TerminalType: "97", Key: "game_win_one_odds", Value: convert.ConvertToString(config.GetGameWinOneOdds())},
			{RobotID: robotID, TerminalType: "97", Key: "game_win_two_odds", Value: convert.ConvertToString(config.GetGameWinTwoOdds())},
			{RobotID: robotID, TerminalType: "97", Key: "game_win_none_odds", Value: convert.ConvertToString(config.GetGameWinNoneOdds())},
			{RobotID: robotID, TerminalType: "97", Key: "game_win_free_odds", Value: convert.ConvertToString(config.GetGameWinFreeOdds())},
		}...,
	)
	// 获取已经同步的配置
	oldConfigs, _ := TerminalConfig{}.GetConfigs(ctx, robotID, "97")
	// 比对已经同步的记录，进行插入或更新
	// 首次插入记录
	if len(oldConfigs) == 0 {
		return dao.TerminalConfig{}.CreateConfigs(ctx, dal.Q, records)
	} else { // 更新或删除
		newRecords := []*models.TerminalConfig{}
		for _, record := range records {
			new := true
			for _, oldConfig := range oldConfigs {
				if oldConfig.Key == record.Key {
					// 更新
					if oldConfig.Value != record.Value {
						dao.TerminalConfig{}.UpdatesConfigs(ctx, dal.Q, robotID, "97", []*models.TerminalConfig{record})
					}
					new = false
					break
				}
			}
			if new {
				newRecords = append(newRecords, record)
			}
		}
		// 批量插入
		if len(newRecords) > 0 {
			return dao.TerminalConfig{}.CreateConfigs(ctx, dal.Q, newRecords)
		}
	}
	return nil
}

// GetConfigs 获取设备配置
func (TerminalConfig) GetConfigs(
	ctx context.Context,
	robotID,
	terminalType string,
) (records []*models.TerminalConfig, err error) {
	records, err = dao.TerminalConfig{}.GetConfigs(ctx, dal.Q, robotID, terminalType, "")
	return
}

// GetConfigLogs 获取配置日志
func (TerminalConfig) GetConfigLogs(
	ctx context.Context,
	robotID,
	terminalType string,
	startTime, endTime int64,
	page, limit int,
) (records []*models.TerminalConfigLog, err error) {
	records, err = dao.TerminalConfig{}.GetConfigLogs(ctx, dal.Q, robotID, terminalType, startTime, endTime, page, limit)
	return
}

// SaveConfig97 保存97配置
func (TerminalConfig) SaveConfig(
	ctx context.Context,
	robotID,
	terminalType string,
	records []*models.TerminalConfig,
	userID string,
) (successCount int64, err error) {
	validKeys := []string{}
	newKeySlice := []string{}
	newKeyValueSlice := []string{}
	oldKeyValueSlice := []string{}
	oldKeyValueMap := make(map[string]string)
	// 获取有效的key
	oldRecords, err := dao.TerminalConfig{}.GetConfigs(ctx, dal.Q, robotID, terminalType, "")
	if err != nil {
		return 0, nil
	}
	for _, record := range oldRecords {
		validKeys = append(validKeys, record.Key)
	}
	// 新值
	// 必须是有效的key
	for _, record := range records {
		if record.Key == "server_url" {
			host := strings.TrimPrefix(strings.TrimPrefix(record.Value, "http://"), "https://")
			if !lo.Contains(ValidServerUrl, host) {
				return 0, errorx.New("无效的服务器地址", -1)
			}
		}
		if lo.Contains(validKeys, record.Key) {
			newKeySlice = append(newKeySlice, record.Key) // 用于获取本次要修改的key
			newKeyValueSlice = append(newKeyValueSlice, record.Key+"-"+record.Value)
		}
	}
	// 对比本次要修改的旧的key
	for _, record := range oldRecords {
		if lo.Contains(newKeySlice, record.Key) {
			oldKeyValueSlice = append(oldKeyValueSlice, record.Key+"-"+record.Value)
			oldKeyValueMap[record.Key] = record.Value
		}
	}
	// 对比要修改的key的前后值
	updatesRecords := []*models.TerminalConfig{}
	updatesRecordLogs := []*models.TerminalConfigLog{}
	{
		diffKeyValues, _ := lo.Difference(newKeyValueSlice, oldKeyValueSlice)
		for _, keyValue := range diffKeyValues {
			updatesRecords = append(updatesRecords, &models.TerminalConfig{
				RobotID:      robotID,
				TerminalType: terminalType,
				Key:          strings.Split(keyValue, "-")[0],
				Value:        strings.Split(keyValue, "-")[1],
			})
			updatesRecordLogs = append(updatesRecordLogs, &models.TerminalConfigLog{
				RobotID:      robotID,
				UserID:       userID,
				TerminalType: terminalType,
				Key:          strings.Split(keyValue, "-")[0],
				OldValue:     oldKeyValueMap[strings.Split(keyValue, "-")[0]],
				NewValue:     strings.Split(keyValue, "-")[1],
			})
		}
	}

	// 保存变更日志
	{
		dao.TerminalConfig{}.CreateConfigLogs(ctx, dal.Q, updatesRecordLogs)
	}

	// 保存配置，并下发
	{
		successCount, _ = dao.TerminalConfig{}.SaveConfigs(ctx, dal.Q, updatesRecords)
		records, _ := dao.TerminalConfig{}.GetConfigs(ctx, dal.Q, robotID, terminalType, "")
		msg := &devicec.Msg{}
		if terminalType == "97" {
			config := config97RecordsToStruct(records)
			config.Fields = newKeySlice
			content, _ := proto.Marshal(config)
			msg.Name = string(config.ProtoReflect().Descriptor().FullName())
			msg.Content = content
			msg.Seq = helper.RandString(16)
			bytes, _ := proto.Marshal(msg)
			pubsub.CMDPublish(robotID, terminalType, "msg", base64.StdEncoding.EncodeToString(bytes))
		}
	}
	return
}

// config97RecordsToStruct 97配置记录转结构体
func config97RecordsToStruct(records []*models.TerminalConfig) *devicec.Config97 {
	config := &devicec.Config97{}
	for _, record := range records {
		switch record.Key {
		case "server_url":
			config.ServerUrl = record.Value
		case "max_purchase_num":
			config.MaxPurchaseNum = convert.StringToInt(record.Value)
		case "pickup_timeout":
			config.PickupTimeout = convert.StringToInt(record.Value)
		case "enable_upload_faceshot":
			config.EnableUploadFaceshot = convert.StringToBool(record.Value)
		case "enable_ignore_overload":
			config.EnableIgnoreOverload = convert.StringToBool(record.Value)
		case "machine_version":
			config.MachineVersion = convert.StringToInt(record.Value)
		case "enable_standby_page":
			config.EnableStandbyPage = convert.StringToBool(record.Value)
		case "enable_side_camera":
			config.EnableSideCamera = convert.StringToBool(record.Value)
		case "enable_nav":
			config.EnableNav = convert.StringToBool(record.Value)
		case "disable_unempty_deliver":
			config.DisableUnemptyDeliver = convert.StringToBool(record.Value)
		case "enable_voice_interaction":
			config.EnableVoiceInteraction = convert.StringToBool(record.Value)
		case "enable_log_print":
			config.EnableLogPrint = convert.StringToBool(record.Value)
		case "enable_important_statement":
			config.EnableImportantStatement = convert.StringToBool(record.Value)
		case "goods_refund_remind":
			config.GoodsRefundRemind = record.Value
		case "reboot_hour":
			config.RebootHour = convert.StringToInt(record.Value)
		case "reboot_min":
			config.RebootMin = convert.StringToInt(record.Value)
		case "deliver_identify_method":
			config.DeliverIdentifyMethod = convert.StringToInt(record.Value)
		case "enable_game":
			config.EnableGame = convert.StringToBool(record.Value)
		case "game_win_odds":
			config.GameWinOdds = convert.StringToFloat32(record.Value)
		case "game_lose_odds":
			config.GameLoseOdds = convert.StringToFloat32(record.Value)
		case "game_draw_odds":
			config.GameDrawOdds = convert.StringToFloat32(record.Value)
		case "game_win_one_odds":
			config.GameWinOneOdds = convert.StringToFloat32(record.Value)
		case "game_win_two_odds":
			config.GameWinTwoOdds = convert.StringToFloat32(record.Value)
		case "game_win_none_odds":
			config.GameWinNoneOdds = convert.StringToFloat32(record.Value)
		case "game_win_free_odds":
			config.GameWinFreeOdds = convert.StringToFloat32(record.Value)
		}
	}
	return config
}
